﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

public  class ToolImmagini
{
    
    #region Funzione Resize
    /// <summary>
    /// Fa il resize dell'immagine dandogli una percetuale;
    /// N.B questo metodo elimina l'immagine source!!! 
    /// </summary>
    /// <param name="img"> Immagine di cui fa il resize -- N.B viene eliminato </param>
    /// <param name="percentage">la percentuale di quanto fare il resize</param>
    /// <returns></returns>
       public static Image Resize(Image img, Double percentage)
       {
           //get the height and width of the image
           int originalW = img.Width;
           int originalH = img.Height;

           //get the new size based on the percentage change
           int resizedW = (int)(originalW * percentage);
           int resizedH = (int)(originalH * percentage);

           //create a new Bitmap the size of the new image
           Bitmap bmp = new Bitmap(resizedW, resizedH);
           //create a new graphic from the Bitmap
           Graphics graphic = Graphics.FromImage((Image)bmp);
           graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
           //draw the newly resized image
           graphic.DrawImage(img, 0, 0, resizedW, resizedH);
           //dispose and free up the resources
           graphic.Dispose();
           img.Dispose();
           //return the image
           return (Image)bmp;
       }
    #endregion
    
    #region Funzioni di Copia
          /// <summary>
       /// // Copia una parte di una bitmap delimitata dal rettangolo 
        /// </summary>
        /// <param name="source">Bitmap source</param>
        /// <param name="part">Rettangolo da cui copiare</param>
        /// <returns></returns>
       public static  Bitmap CopyBitmap(Bitmap source, Rectangle part)
       {
           //int t1 = DateTime.Now.Millisecond;
           if (part.Width == 0 || part.Height == 0)
           {
               return new Bitmap(source);
           }
           Bitmap bmp = new Bitmap(part.Width, part.Height);
           Graphics g = Graphics.FromImage(bmp);
           g.DrawImage(source, new Rectangle(0, 0, part.Width, part.Height), part, GraphicsUnit.Pixel);
           g.Dispose();
           //int t2 = DateTime.Now.Millisecond - t1;
           //Console.WriteLine("Tempo CopyBitmap: " + t2);
           return bmp;
       }
     
       public static Bitmap CopyOnbackground(Bitmap source, Bitmap tocopy, Rectangle part)  {

           //int t1 = DateTime.Now.Millisecond;
           Graphics g = Graphics.FromImage(source);  
           g.DrawImage(tocopy, new Point(part.X, part.Y));
           g.Dispose();
           //int t2 = DateTime.Now.Millisecond-t1;
           //Console.WriteLine("Tempo CopyonBacgroud: " + t2);
           return source;
       }

    #endregion

    
    public static byte[] ImagetoByteArray(Bitmap bmp)
       {
           MemoryStream ms = new MemoryStream();
           // Save to memory using the Jpeg format
           bmp.Save(ms, ImageFormat.Jpeg);
           // read to end
           byte[] bmpBytes = ms.GetBuffer();
           bmp.Dispose();
           ms.Close();
           return bmpBytes;
       }

    private delegate Rectangle DiffImageDelegate(Bitmap old, Bitmap img, Rectangle r);

    public static Rectangle DiffImage(Bitmap old, Bitmap img)
     {


         if (old.Size != img.Size)
         {
             throw new DifferentSizeException();
         }
         else
         {
             Rectangle rect = new Rectangle(0, 0, old.Width, old.Height);


             int x = 0;
             int y = 0;



             Bitmap old2 = old.Clone(rect, PixelFormat.Format24bppRgb);
             Bitmap img2 = img.Clone(rect, PixelFormat.Format24bppRgb);

             Point alto_sinistro = new Point(img.Width, img.Height);
             Point basso_destro = new Point(0, 0);

             BitmapData bmpData = old2.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
             BitmapData bmpData2 = img2.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);


             unsafe // è unsafe perché utilizzo dei puntatori diretti
             {

                 byte* row_old = (byte*)bmpData.Scan0;
                 byte* row_new = (byte*)bmpData2.Scan0;

                 int Stride = bmpData.Stride;

                 for (y = 0; y < bmpData.Height; y++)
                 {
                     for (x = 0; x < bmpData.Width; x++)
                     {
                         if (row_old[(y * Stride) + (x * 3)] != row_new[(y * Stride) + (x * 3)] || row_old[(y * Stride) + (x * 3) + 1] != row_new[(y * Stride) + (x * 3) + 1] || row_old[(y * Stride) + (x * 3) + 2] != row_new[(y * Stride) + (x * 3) + 2])
                         {
                             basso_destro.X = Math.Max(x, basso_destro.X);
                             basso_destro.Y = Math.Max(y, basso_destro.Y);
                             alto_sinistro.X = Math.Min(alto_sinistro.X, x);
                             alto_sinistro.Y = Math.Min(alto_sinistro.Y, y);
                         }
                     }
                 }
                 old2.UnlockBits(bmpData);
                 img2.UnlockBits(bmpData2);
                 old2.Dispose();
                 img2.Dispose();
             }
             Rectangle r = new Rectangle(alto_sinistro.X, alto_sinistro.Y, Math.Abs(alto_sinistro.X - basso_destro.X) + 1, Math.Abs(alto_sinistro.Y - basso_destro.Y) + 1);
             return r;
         }
     }

    /// <summary>
    /// Interfaccia publica che mi calcola le differenze tra 2 immagini usando il thread pool, il risultato è identico a quello di 
    /// DiffImage
    /// </summary>
    /// <param name="old"></param>
    /// <param name="img"></param>
    /// <returns></returns>
    public static Rectangle DiffImage2(Bitmap old, Bitmap img)
    {
        if (old.Size != img.Size)
        {
            throw new DifferentSizeException();
        }
        DiffImageThreadPool t1 = new DiffImageThreadPool();
        return t1.Analise(old,img);
    }

    /// <summary>
    /// Crea una nuova immagine con lo sfondo dato dall'immagine + un acroce sul punto passatogli
    /// 
    /// </summary>
    /// <param name="orig"> Immagine di sfondo</param>
    /// <param name="mouse_pos">Punto su cui inserire il mouse</param>
    /// <returns></returns>
    public static Image DisegnaMouse(Image orig, Point mouse_pos)
    {
        Bitmap new_img = new Bitmap(orig);
        Graphics g = Graphics.FromImage(new_img);

        g.DrawLine(new Pen(Color.FromArgb(170, 0, 0, 0),5), new Point(mouse_pos.X - 15, mouse_pos.Y), new Point(mouse_pos.X + 15, mouse_pos.Y));
        g.DrawLine(new Pen(Color.FromArgb(170, 0, 0, 0),5), new Point(mouse_pos.X, mouse_pos.Y - 15), new Point(mouse_pos.X, mouse_pos.Y + 15));


        g.Dispose();
        return new_img;
    }

    /// <summary>
    /// Funzione che data un immagine me la restituisce compressa
    /// </summary>
    /// <param name="image"></param>
    /// <returns></returns>
    public static Image Comprimi(Bitmap image)
    {
        Image m = new Bitmap(image);
        EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality,(long) 35);
        ImageCodecInfo jpegCodec = getEncoderInfo("image/jpeg");
        if (jpegCodec == null)
            return null;
        EncoderParameters encoderParams = new EncoderParameters(1);
        encoderParams.Param[0] = qualityParam;
        Stream stream = new MemoryStream();
        image.Save(stream, jpegCodec, encoderParams);
        return Image.FromStream(stream);
    }
    private static ImageCodecInfo getEncoderInfo(string mimeType)
    {
        ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders();
        for (int i = 0; i <codecs.Length; i++)
            if (codecs[i].MimeType == mimeType)
                return codecs[i];

        return null;
    }


    /// <summary>
    /// Classe che mi maschera il fatto di usare l thread pool, dentro la classe ci sono 
    /// gli eventi su cui il threadpool deve aspettare.
    /// </summary>
    public class DiffImageThreadPool
    {

        #region Variabili
        private Rectangle[] Diffrect;
        private Point[] alto_sinistro;
        private Point[] basso_destro;
        private ManualResetEvent[] resetEvents;
        #endregion

        #region Costruttore
        public DiffImageThreadPool()
        {
            this.Diffrect = new Rectangle[4];
            this.basso_destro = new Point[4];
            this.alto_sinistro = new Point[4];
            this.resetEvents = new ManualResetEvent[4];
            for (int i = 0; i < 4; i++)
                resetEvents[i] = new ManualResetEvent(false);
        }
        #endregion

        /// <summary>
        /// Funzione privata di analisi, In questa funzione si analizzano le differenze nelle singole porzioni di schermo
        /// </summary>
        /// <param name="o">Vuole un </param>
        private void DiffImage_thread_pool(Object o)
        {
            int x = 0;
            int y = 0;
            DiffImageHelper helper = (DiffImageHelper)o;

            Bitmap old2 = helper.old_img.Clone(helper.rect, PixelFormat.Format24bppRgb);
            Bitmap img2 = helper.new_img.Clone(helper.rect, PixelFormat.Format24bppRgb);

            Point alto_sinistro = new Point(helper.rect.Width, helper.rect.Height);
            Point basso_destro = new Point(0, 0);
            BitmapData bmpData = old2.LockBits(new Rectangle(0, 0, helper.rect.Width, helper.rect.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            BitmapData bmpData2 = img2.LockBits(new Rectangle(0, 0, helper.rect.Width, helper.rect.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            unsafe // è unsafe perché utilizzo dei puntatori diretti
            {
                byte* row_old = (byte*)bmpData.Scan0;
                byte* row_new = (byte*)bmpData2.Scan0;
                int Stride = bmpData.Stride;
                for (y = 0; y < helper.rect.Height; y++)
                {
                    for (x = 0; x < helper.rect.Width; x++)
                    {
                        if (row_old[(y * Stride) + (x * 3)] != row_new[(y * Stride) + (x * 3)] || row_old[(y * Stride) + (x * 3) + 1] != row_new[(y * Stride) + (x * 3) + 1] || row_old[(y * Stride) + (x * 3) + 2] != row_new[(y * Stride) + (x * 3) + 2])
                        {
                            basso_destro.X = Math.Max(x, basso_destro.X);
                            basso_destro.Y = Math.Max(y, basso_destro.Y);
                            alto_sinistro.X = Math.Min(alto_sinistro.X, x);
                            alto_sinistro.Y = Math.Min(alto_sinistro.Y, y);
                        }
                    }
                }
                old2.UnlockBits(bmpData);
                img2.UnlockBits(bmpData2);
                old2.Dispose();
                img2.Dispose();
            }

            if (alto_sinistro.X == helper.rect.Width && alto_sinistro.Y == helper.rect.Height)
                alto_sinistro = new Point(50000, 50000);
            this.basso_destro[helper.index] = basso_destro;
            this.alto_sinistro[helper.index] = alto_sinistro;

            //Console.WriteLine("Trovato altodestro"+alto_sinistro);
            //Console.WriteLine("Trovato bassosinistro"+basso_destro);
            resetEvents[helper.index].Set();
        }

        /// <summary>
        /// Interfaccia verso l'esterno, Questa funzione divide le immagini in 4 sottoimmagini e le invia al thread pool
        /// dopodiché si pone in attesa del risultato che è il rettangolo che contiene tutte le differenze dell'immagine
        /// </summary>
        /// <param name="old">Immagine di backgrround</param>
        /// <param name="img">Immagine da confrontare con il background</param>
        /// <returns></returns>
        public Rectangle Analise(Bitmap old, Bitmap img)
        {

            // Creo delle copie poiché se uso semplicemente old e img, queste vengono bloccate dal primo thread del thread pool 
            Bitmap old1 = (Bitmap)old.Clone();
            Bitmap img1 = (Bitmap)img.Clone();
            Bitmap old2 = (Bitmap)old.Clone();
            Bitmap img2 = (Bitmap)img.Clone();
            Bitmap old3 = (Bitmap)old.Clone();
            Bitmap img3 = (Bitmap)img.Clone();
            Bitmap old4 = (Bitmap)old.Clone();
            Bitmap img4 = (Bitmap)img.Clone();

            ThreadPool.QueueUserWorkItem(new WaitCallback(DiffImage_thread_pool), new DiffImageHelper(old1, img1, new Rectangle(0, 0, old.Width / 2, old.Height / 2), 0));
            ThreadPool.QueueUserWorkItem(new WaitCallback(DiffImage_thread_pool), new DiffImageHelper(old2, img2, new Rectangle(old.Width / 2, 0, old.Width / 2, old.Height / 2), 1));
            ThreadPool.QueueUserWorkItem(new WaitCallback(DiffImage_thread_pool), new DiffImageHelper(old3, img3, new Rectangle(0, old.Height / 2, old.Width / 2, old.Height / 2), 2));
            ThreadPool.QueueUserWorkItem(new WaitCallback(DiffImage_thread_pool), new DiffImageHelper(old4, img4, new Rectangle(old.Width / 2, old.Height / 2, old.Width / 2, old.Height / 2), 3));

            // Aspetto che tutti i thread abbiano terminato
            WaitHandle.WaitAll(resetEvents);

            // cancello le copie
            old1.Dispose();
            img1.Dispose();
            old2.Dispose();
            img2.Dispose();
            old3.Dispose();
            img3.Dispose();
            old4.Dispose();
            img4.Dispose();


            // calcolo le coordinate del rettangolo massimo a partire da quelle restituite dai vari sottothread
            basso_destro[1].X += old.Width / 2;
            alto_sinistro[1].X += old.Width / 2;
            basso_destro[2].Y += old.Height / 2;
            alto_sinistro[2].Y += old.Height / 2;
            basso_destro[3].X += old.Width / 2;
            alto_sinistro[3].X += old.Width / 2;
            basso_destro[3].Y += old.Height / 2;
            alto_sinistro[3].Y += old.Height / 2;

            Point p1 = new Point(Math.Max(Math.Max(basso_destro[0].X, basso_destro[1].X), Math.Max(basso_destro[2].X, basso_destro[3].X)), Math.Max(Math.Max(basso_destro[0].Y, basso_destro[1].Y), Math.Max(basso_destro[2].Y, basso_destro[3].Y)));
            Point p2 = new Point(Math.Min(Math.Min(alto_sinistro[0].X, alto_sinistro[1].X), Math.Min(alto_sinistro[2].X, alto_sinistro[3].X)), Math.Min(Math.Min(alto_sinistro[0].Y, alto_sinistro[1].Y), Math.Min(alto_sinistro[2].Y, alto_sinistro[3].Y)));
            Rectangle r = new Rectangle(p2, new Size(p1.X - p2.X + 1, p1.Y - p2.Y + 1));
            if (r.Width <= 0 || r.Height <= 0) // Non ci sono differenze
            {
                r = new Rectangle(0, 0, 2, 2); // mando 4 byte
            }

            //Console.WriteLine("Rectangle is:" + r); 
            return r;
        }


        /// <summary>
        /// AL thread pool posso passare un oggetto singolo, questa classe serve a creare un oggetto con tutte le informazioni
        /// necessarie al thread pool.
        /// </summary>
        class DiffImageHelper
        {
            public Bitmap old_img;
            public Bitmap new_img;
            public Rectangle rect;
            public int index;
            public DiffImageHelper(Bitmap old, Bitmap img, Rectangle rectangle, int p)
            {
                this.old_img = old;
                this.new_img = img;
                this.rect = rectangle;
                this.index = p;
            }
        }
    }
}

